#pragma once

#include <vex/functional/contract/delegate.h>
#include <vex/core/config.h>
#include <utility>
#include <type_traits>

namespace vex { namespace functional { namespace implementation {
    namespace detail {

        template< class TFunction, class TDelegateContract>
        class VEX_ABSTRACT delegate_default_impl_base : public TDelegateContract {
            delegate_default_impl_base(delegate_default_impl_base const &);
            delegate_default_impl_base& operator=(delegate_default_impl_base const &);
        protected:
            typedef typename std::decay<TFunction>::type function_type;
            mutable function_type m_func;

            template<class UFunction>
            explicit delegate_default_impl_base(UFunction && p_func) 
                : m_func(std::forward<UFunction>(p_func)) {}

            delegate_default_impl_base(delegate_default_impl_base&& p_other)
                : m_func(std::move(p_other.m_func)) {}

            ~delegate_default_impl_base() {}
        };
    }

#define VEX_DELEGATE_DEFAULT_IMPL_BODY(VexDelegateImplClassName) \
    typedef typename base_type::function_type function_type; \
    \
    template<class UFunction> \
    VexDelegateImplClassName(UFunction&& p_func) \
        : base_type(std::forward<UFunction>(p_func)) {} \
    \
    VexDelegateImplClassName(VexDelegateImplClassName&& p_other) \
        : base_type(std::move(p_other)) {} \
    \
    ~VexDelegateImplClassName() {}

    template<
        class TSignature, 
        class TFunction, 
        class TDelegateContract = contract::delegate<TSignature> 
    > class delegate_default_impl;

    template<class TRet, class TFunction, class TDelegateContract> 
    class delegate_default_impl<TRet (), TFunction, TDelegateContract> 
        : public detail::delegate_default_impl_base<TFunction, TDelegateContract>
    {
    protected:
        typedef detail::delegate_default_impl_base<TFunction, TDelegateContract> base_type;

        VEX_DELEGATE_DEFAULT_IMPL_BODY(delegate_default_impl);
    public:
        virtual TRet invoke() const 
        {
            return this->m_func();
        }
    };

    template<class TRet, class TArg1, class TFunction, class TDelegateContract> 
    class delegate_default_impl<TRet (TArg1), TFunction, TDelegateContract> 
        : public detail::delegate_default_impl_base<TFunction, TDelegateContract>
    {
    protected:
        typedef detail::delegate_default_impl_base<TFunction, TDelegateContract> base_type;

        VEX_DELEGATE_DEFAULT_IMPL_BODY(delegate_default_impl);
    public:
        virtual TRet invoke(TArg1 p_arg1) const 
        {
            return this->m_func(std::forward<TArg1>(p_arg1));
        }
    };

    template<class TRet, class TArg1, class TArg2, class TFunction, class TDelegateContract> 
    class delegate_default_impl<TRet (TArg1, TArg2), TFunction, TDelegateContract> 
        : public detail::delegate_default_impl_base<TFunction, TDelegateContract>
    {
    protected:
        typedef detail::delegate_default_impl_base<TFunction, TDelegateContract> base_type;

        VEX_DELEGATE_DEFAULT_IMPL_BODY(delegate_default_impl);
    public:
        virtual TRet invoke(TArg1 p_arg1, TArg2 p_arg2) const 
        {
            return this->m_func(
                std::forward<TArg1>(p_arg1),
                std::forward<TArg2>(p_arg2));
        }
    };

    template<class TRet, class TArg1, class TArg2, class TArg3, class TFunction, class TDelegateContract> 
    class delegate_default_impl<TRet (TArg1, TArg2, TArg3), TFunction, TDelegateContract> 
        : public detail::delegate_default_impl_base<TFunction, TDelegateContract>
    {
    protected:
        typedef detail::delegate_default_impl_base<TFunction, TDelegateContract> base_type;

        VEX_DELEGATE_DEFAULT_IMPL_BODY(delegate_default_impl);
    public:
        virtual TRet invoke(TArg1 p_arg1, TArg2 p_arg2, TArg3 p_arg3) const 
        {
            return this->m_func(
                std::forward<TArg1>(p_arg1),
                std::forward<TArg2>(p_arg2),
                std::forward<TArg3>(p_arg3));
        }
    };

    template<class TRet, class TArg1, class TArg2, class TArg3, class TArg4, class TFunction, class TDelegateContract> 
    class delegate_default_impl<TRet (TArg1, TArg2, TArg3, TArg4), TFunction, TDelegateContract> 
        : public detail::delegate_default_impl_base<TFunction, TDelegateContract>
    {
    protected:
        typedef detail::delegate_default_impl_base<TFunction, TDelegateContract> base_type;

        VEX_DELEGATE_DEFAULT_IMPL_BODY(delegate_default_impl);
    public:
        virtual TRet invoke(TArg1 p_arg1, TArg2 p_arg2, TArg3 p_arg3, TArg4 p_arg4) const 
        {
            return this->m_func(
                std::forward<TArg1>(p_arg1),
                std::forward<TArg2>(p_arg2),
                std::forward<TArg3>(p_arg3),
                std::forward<TArg4>(p_arg4));
        }
    };

    template<class TRet, class TArg1, class TArg2, class TArg3, class TArg4, class TArg5, class TFunction, class TDelegateContract> 
    class delegate_default_impl<TRet (TArg1, TArg2, TArg3, TArg4, TArg5), TFunction, TDelegateContract> 
        : public detail::delegate_default_impl_base<TFunction, TDelegateContract>
    {
    protected:
        typedef detail::delegate_default_impl_base<TFunction, TDelegateContract> base_type;

        VEX_DELEGATE_DEFAULT_IMPL_BODY(delegate_default_impl);
    public:
        virtual TRet invoke(TArg1 p_arg1, TArg2 p_arg2, TArg3 p_arg3, TArg4 p_arg4, TArg5 p_arg5) const 
        {
            return this->m_func(
                std::forward<TArg1>(p_arg1),
                std::forward<TArg2>(p_arg2),
                std::forward<TArg3>(p_arg3),
                std::forward<TArg4>(p_arg4),
                std::forward<TArg5>(p_arg5));
        }
    };
}}}